using System;
using System.Collections;
using CodeAngel.Domain.Framework;
using System.Data;
using Smo = Microsoft.SqlServer.Management.Smo;
using CodeAngel.Domain.Classes;

namespace CodeAngel.Domain
{
	public partial class PropertyDef //: IName
	{
		#region SetDefaultPropertyValues

        /// <summary>
        /// Sets the properties of a new entity to its default values.
        /// </summary>
		internal override void SetDefaultPropertyValues()
		{
            if (this.ParentEntity is EntityDef)
                this.EntityDef = this.ParentEntity as EntityDef;
            this.SetFriendlyIdAsync();
            this.PropertyDefID = Guid.NewGuid();
            this.IsPrimaryKey = false;
            this.IsNameKeyword = false;
            this.IsNullable = false;
            //this.MustBeUnique = false;
            //this.SqlIsIdentity = false;

            this.DataType = "string";
            //this.SqlDataLength = 50;
            //this.SqlDataPrecision = 0;
            //this.SqlDataScale = 0;
		}

		#endregion

        #region NameChanged

        /// <summary>
        /// Handles when the Name changes and determines if default values for related properties
        /// can be set, and if so, sets them.
        /// </summary>
        partial void NameChanged(string previousValue, string newValue)
        {
            if (string.IsNullOrWhiteSpace(previousValue) && !string.IsNullOrWhiteSpace(newValue))
            {
                this.FriendlyName = this.GetDefaultFriendlyName();
                this.DatabaseColumnName = this.GetDefaultColumnName();
            }
        }

        /// <summary>
        /// Gets the default setting for the FriendlyName property based on the current Name.
        /// </summary>
        private string GetDefaultFriendlyName()
        {
            ArrayList Words = new ArrayList();
            System.Text.StringBuilder WordBuilder = new System.Text.StringBuilder();
            char PreviousChar = ' ';
            char ThisChar = '\0';
            foreach (char ThisChar_loopVariable in this.Name)
            {
                ThisChar = ThisChar_loopVariable;
                if (char.IsUpper(ThisChar) && !char.IsUpper(PreviousChar))
                {
                    if (WordBuilder != null) Words.Add(WordBuilder.ToString());
                    WordBuilder = new System.Text.StringBuilder();
                }
                WordBuilder.Append(ThisChar);
                PreviousChar = ThisChar;
            }
            Words.Add(WordBuilder.ToString());

            //Put the words together with a space between each one.
            string ThisWord = null;
            string ThisString = null;
            foreach (string ThisWord_loopVariable in Words)
            {
                ThisWord = ThisWord_loopVariable;
                ThisString += ThisWord + " ";
            }

            return ThisString.Trim();

        }

        /// <summary>
        /// Gets the default setting for the ColumnName property based on the current Name and DataType.
        /// </summary>
        private string GetDefaultColumnName()
        {
            if (!string.IsNullOrWhiteSpace(this.DataType))
            {
                if (this.IsEntityType)
                {
                    return this.Name + "ID";
                }
                else if (this.IsCollectionType)
                {
                    return string.Empty;
                }
                else
                {
                    return this.Name;
                }

            }
            else
            {
                return this.Name;
            }

        }

        #endregion

        #region DataTypeChanged

        /// <summary>
        /// Handles when the DataType changes and may set the SqlDataType property to a default value.
        /// </summary>
        partial void DataTypeChanged(string previousValue, string newValue)
        {
            //if (!string.IsNullOrWhiteSpace(newValue) && string.IsNullOrWhiteSpace(this.SqlDataType))
                this.SqlDataType = this.GetDefaultSqlDataType(newValue);

            this.DatabaseColumnName = this.GetDefaultColumnName();

            this.IsForeignKeyPropertyDefRequired = this.IsEntityOrCollectionType;
        }

        /// <summary>
        /// Gets the default SQL data type for the identified CLR data type.
        /// </summary>
        public string GetDefaultSqlDataType(string clrDataType)
        {
            switch (clrDataType)
            {
                case "bool":
                    return "bit";

                case "DateTime":
                    return "datetime";

                case "decimal":
                case "single":
                case "double":
                    return "decimal";

                case "Guid":
                    return "uniqueidentifier";

                case "int":
                    return "int";

                case "long":
                    return "bigint";

                case "string":
                    return "varcharmax";

                //case "currency":
                //    return "money";

                //case "Binary":
                //    return "binary";


                //default:
                //    break;
                //Assume an Entity
                //return "uniqueidentifier";
            }


            if (this.IsEntityType) return "uniqueidentifier";
            return string.Empty;
        }

        #endregion

        #region IsForeignKeyPropertyDefRequired

        /// <summary>
        /// Gets or sets the IsForeignKeyPropertyDefRequired.
        /// </summary>
        public bool IsForeignKeyPropertyDefRequired
        {
            get { return this._IsForeignKeyPropertyDefRequired; }
            private set
            {
                if (value != this._IsForeignKeyPropertyDefRequired)
                {
                    this._IsForeignKeyPropertyDefRequired = value;
                    this.RaisePropertyChanged("IsForeignKeyPropertyDefRequired");
                }
            }
        }
        private bool _IsForeignKeyPropertyDefRequired;

        #endregion

        #region IsEditing

        /// <summary>
        /// Gets or sets the IsEditing.
        /// </summary>
        public bool IsEditing
        {
            get { return this._IsEditing; }
            set
            {
                if (value != this._IsEditing)
                {
                    this._IsEditing = value;
                    this.RaisePropertyChanged("IsEditing");
                }
            }
        }
        private bool _IsEditing = false;

        #endregion

        #region Data Type Helpers

        /// <summary>
        /// Identifies if the data type of this PropertyDef is an Entity.
        /// </summary>
        public bool IsEntityType
        {
            get
            {
                return this.DataTypeEntityDef != null && !this.IsCollectionType;
            }
        }

        /// <summary>
        /// Identifies if the data type of this PropertyDef is an EntityCollection.
        /// </summary>
        public bool IsCollectionType
        {
            get
            {

                if (this.DataType.Contains("Collection"))
                {
                    string EntityName = this.DataType.Replace("Collection", "");
                    return this.EntityDef.Domain.EntityDefs.Find(EntityName) != null;
                }
                return false;
            }
        }

        /// <summary>
        /// Identifies if the data type of this PropertyDef is an Entity or EntityCollection.
        /// </summary>
        public bool IsEntityOrCollectionType
        {
            get { return this.IsEntityType || this.IsCollectionType; }
        }

        /// <summary>
        /// Identifies the EntityDef object for the DataType that this PropertyDef is
        /// delcared as.  Returns null if this PropertyDef's DataType is not based on an EntityDef.
        /// </summary>
        public EntityDef DataTypeEntityDef
        {
            get
            {
                this._dataTypeEntityDef = this.EntityDef.Domain.EntityDefs.Find(this.DataType);
                if (this._dataTypeEntityDef == null)
                {
                    string EntityName = this.DataType.Replace("Collection", "");
                    this._dataTypeEntityDef = this.EntityDef.Domain.EntityDefs.Find(EntityName);
                }
                return this._dataTypeEntityDef;
            }
        }
        private EntityDef _dataTypeEntityDef;

        /// <summary>
        /// Identifies if the data type of this PropertyDef is scalar.
        /// </summary>
        public bool IsScalarType
        {
            get { return !this.IsEntityType && !this.IsCollectionType; }
        }

        /// <summary>
        /// Identifies if the data type of this PropertyDef is an Entity or Scalar.
        /// </summary>
        public bool IsScalarOrEntityType
        {
            get { return this.IsScalarType || this.IsEntityType; }
        }

        /////
        ////NAME: DataTypeEntity
        ////PURPOSE: If the data type of this property definition is based on a Entity (either
        ////a Entity or collection) then this property will identify that Entity.
        /////
        //private EntityDef mDataTypeED;
        //public EntityDef DataTypeEntity
        //{
        //    get
        //    {
        //        this.mDataTypeED = this.EntityDef.Domain.EntityDefs.Find(this.DataType);
        //        if ((this.mDataTypeED == null))
        //        {
        //            string EntityName = this.DataType.Replace("Collection", "");
        //            this.mDataTypeED = this.EntityDef.Domain.EntityDefs.Find(EntityName);
        //        }
        //        return this.mDataTypeED;
        //    }
        //}


        /// <summary>
        /// Gets the SMO DataType for this PropertyDef.
        /// </summary>
        public Smo.DataType SmoDataType
        {
            get
            {
                switch (this.SqlDataType.ToLower())
                {
                    case "bigint":
                        return Microsoft.SqlServer.Management.Smo.DataType.BigInt;
                    case "binary":
                        return Smo.DataType.Binary(this.SqlDataLength.Value);
                    case "bit":
                        return Smo.DataType.Bit;
                    case "char":
                        return Smo.DataType.Char(this.SqlDataLength.Value);
                    case "datetime":
                        return Smo.DataType.DateTime;
                    case "decimal":
                        return Smo.DataType.Decimal(this.SqlDataScale.Value, this.SqlDataPrecision.Value);
                    case "float":
                        return Smo.DataType.Float;
                    case "image":
                        return Smo.DataType.Image;
                    case "int":
                        return Smo.DataType.Int;
                    case "money":
                        return Smo.DataType.Money;
                    case "nchar":
                        return Smo.DataType.NChar(this.SqlDataLength.Value);
                    case "ntext":
                        return Smo.DataType.NText;
                    case "numeric":
                        return Smo.DataType.Numeric(this.SqlDataScale.Value, this.SqlDataPrecision.Value);
                    case "nvarchar":
                        return Smo.DataType.NVarChar(this.SqlDataLength.Value);
                    case "nvarcharmax":
                        return Smo.DataType.NVarCharMax;
                    case "real":
                        return Smo.DataType.Real;
                    case "smalldatetime":
                        return Smo.DataType.SmallDateTime;
                    case "smallint":
                        return Smo.DataType.SmallInt;
                    case "smallmoney":
                        return Smo.DataType.SmallMoney;
                    case "text":
                        return Smo.DataType.Text;
                    case "timestamp":
                        return Smo.DataType.Timestamp;
                    case "tinyint":
                        return Smo.DataType.TinyInt;
                    case "uniqueidentifier":
                        return Smo.DataType.UniqueIdentifier;
                    case "varbinarymax":
                        return Smo.DataType.VarBinaryMax;
                    case "varcharmax":
                        return Smo.DataType.VarCharMax;
                    case "variant":
                        return Smo.DataType.Variant;
                    case "varbinary":
                        return Smo.DataType.VarBinary(this.SqlDataLength.Value);
                    case "varchar":
                        return Smo.DataType.VarChar(this.SqlDataLength.Value);
                    case "xml":
                        return Smo.DataType.Xml("String");
                    default:
                        throw new Exception("SMO Data Type not mapped: '" + this.SqlDataType + "' for property: '" + this.Name + "'.");
                }
            }
        }

        /// <summary>
        /// Gets the SqlDbType for this PropertyDef.
        /// </summary>
        public SqlDbType SqlDbType
        {
            get
            {
                switch (this.SqlDataType.ToLower())
                {
                    case "bigint": return SqlDbType.BigInt;
                    case "binary": return SqlDbType.Binary;
                    case "bit": return SqlDbType.Bit;
                    case "char": return SqlDbType.Char;
                    case "datetime": return SqlDbType.DateTime;
                    case "decimal": return SqlDbType.Decimal;
                    case "float": return SqlDbType.Float;
                    case "image": return SqlDbType.Image;
                    case "int": return SqlDbType.Int;
                    case "money": return SqlDbType.Money;
                    case "nchar": return SqlDbType.NChar;
                    case "ntext": return SqlDbType.NText;
                    case "nvarchar": return SqlDbType.NVarChar;
                    case "nvarcharmax": return SqlDbType.NVarChar;
                    case "real": return SqlDbType.Real;
                    case "smalldatetime": return SqlDbType.SmallDateTime;
                    case "smallint": return SqlDbType.SmallInt;
                    case "smallmoney": return SqlDbType.SmallMoney;
                    case "text": return SqlDbType.Text;
                    case "timestamp": return SqlDbType.Timestamp;
                    case "tinyint": return SqlDbType.TinyInt;
                    case "uniqueidentifier": return SqlDbType.UniqueIdentifier;
                    case "varbinarymax": return SqlDbType.VarBinary;
                    case "varcharmax": return SqlDbType.VarChar;
                    case "variant": return SqlDbType.Variant;
                    case "varbinary": return SqlDbType.VarBinary;
                    case "varchar": return SqlDbType.VarChar;
                    case "xml": return SqlDbType.Xml;
                    default:
                        throw new Exception("SQL DB Type not mapped: '" + this.SqlDataType + "' for property: '" + this.Name + "'.");
                }
            }
        }


        #endregion

        #region HasDefaultValue

        /// <summary>
        /// Identifies if this PropertyDef has default value associated with it.
        /// </summary>
        public bool HasDefaultValue
        {
            get { return this.DefaultValue.IsNotNullOrEmpty(); }
        }

        #endregion

        #region HasSqlDefaultValue
        
        /// <summary>
        /// Identifies if this PropertyDef has SQL default value associated with it.
        /// </summary>
        public bool HasSqlDefaultValue
        {
            get { return this.SqlDefaultValue.IsNotNullOrEmpty(); }
        }

        #endregion

        #region Names

        /// <summary>
        /// Used to help identify various name representations throughout the code generation process.
        /// </summary>
        public PropertyDefNames Names
        {
            get
            {
                if (this._names == null) this._names = new PropertyDefNames(this);
                return this._names;
            }
        }
        private PropertyDefNames _names;

        #endregion

        #region Dt

        /// <summary>
        /// Used to help identify various data type representations throughout the code generation process.
        /// </summary>
        public DataType Dt
        {
            get
            {
                if (this._dataType == null) this._dataType = new DataType(this);
                return this._dataType;
            }
        }
        private DataType _dataType;

        #endregion

        #region SqlDataReaderGet
        
        /// <summary>
        /// Identifies the function name used by the SQLDataReader class when reading this propety's
        /// value from a ADO.NET resultset.
        /// </summary>
        public string SqlDataReaderGet
        {
            get
            {
                switch (this.DataType)
                {
                    case "bool":
                        return "GetBoolean";
                    case "DateTime":
                        return "GetDateTime";
                    case "decimal":
                        return "GetDecimal";
                    case "Guid":
                        return "GetGuid";
                    case "int":
                        return "GetInt32";
                    case "long":
                        return "GetInt64";
                    case "Single":
                        return "GetSingle";
                    case "double":
                        return "GetDouble";
                    case "string":
                        return "GetString";
                    case "Binary":
                        return "GetBytes";
                    case "Currency":
                        return "GetDecimal";
                    default:
                        System.Diagnostics.Debugger.Break();
                        throw new Exception("DataType not mapped to SQL Data Reader Get: " + this.DataType);
                }
            }
        }

        #endregion

        #region IName Members

        public bool IsKeyword
        {
            get
            {
                return this.IsNameKeyword;
            }
            set
            {
                this.IsNameKeyword = value;
            }
        }

        public bool IsEntityDef
        {
            get { return this.Dt.IsEntity; }
        }

        #endregion

        #region GetRelationConstantValue
        
        /// <summary>
        /// Gets the string the identifies the relation between ...
        /// </summary>
        public string GetRelationConstantValue()
        {
            return this.EntityDef.Name + "To" + this.Name + "Relation";
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return this.Name;
        }

        #endregion
    }

    #region Names

    /// <summary>
    /// 
    /// </summary>
    public class PropertyDefNames 
    {
        #region Constructors

        public PropertyDefNames(PropertyDef thisPD)
        {
            this.PropertyDef = thisPD;
        }
        public PropertyDef PropertyDef { get; set; }

        #endregion

        #region BackingProperty

        /// <summary>
        /// Each property for an Entity uses a backing property instance to manage its state.
        /// This identifies the name of that backing property.
        /// </summary>
        /// <returns></returns>
        public string BackingProperty()
        {
            return this.PropertyDef.Name + "Property";
        }

        public string BackingPropertyField()
        {
            return "_" + this.PropertyDef.Name + "Property";
        }

        #endregion

        #region Field

        /// <summary>
        /// Gets the name of this PropertyDef as used as a field in a table.
        /// </summary>
        public string Field()
        {
            if (this.PropertyDef.IsEntityDef)
            {
                return this.PropertyDef.Name + "ID";
            }
            else
            {
                return this.PropertyDef.Name;
            }
        }

        #endregion

        #region Criterion

        /// <summary>
        /// Gets the name of this PropertyDef as used as a Criterion in a filter.
        /// </summary>
        public string Criterion()
        {
            if (this.PropertyDef.IsEntityDef)
            {
                return this.PropertyDef.Name + "ID";
            }
            else
            {
                return this.PropertyDef.Name;
            }
        }

        #endregion

    }

    #endregion

    /// <summary>
    /// To help identify how a data type of a declaration should be written throughout the code 
    /// generation process under a variety of contexts.
    /// </summary>
    public class DataType
    {

        private PropertyDef mProperty;
        public DataType(PropertyDef thisPropertyDef)
        {
            this.mProperty = thisPropertyDef;
        }

        ///
        //NAME: IsEntity
        //PURPOSE: To identify if this data type is a Entity.
        ///
        public bool IsEntity
        {
            get { return this.mProperty.EntityDef.Domain.EntityDefs.Find(this.mProperty.DataType) != null; }
        }

        ///
        //NAME: IsEntityCollection
        //PURPOSE: To identify if this data type is a Entity collection.
        ///
        public bool IsEntityCollection
        {
            get
            {
                if (this.mProperty.DataType.Contains("Collection"))
                {
                    string BaseName = this.mProperty.DataType.Replace("Collection", "");
                    return this.mProperty.EntityDef.Domain.EntityDefs.Find(BaseName) != null;
                }
                return false;
            }
        }

        ///
        //NAME: Entity
        //PURPOSE: To return the Entity this data type is based on, if it is based on
        //a Entity.
        ///
        public EntityDef Entity
        {
            get
            {
                if (this.IsEntity)
                {
                    return this.mProperty.EntityDef.Domain.EntityDefs.Find(this.mProperty.DataType);

                }
                else if (this.IsEntityCollection)
                {
                    string BaseName = this.mProperty.DataType.Replace("Collection", "");
                    return this.mProperty.EntityDef.Domain.EntityDefs.Find(BaseName);

                }
                else
                {
                    return null;
                }
            }
        }

        ///
        //NAME: ToName
        //PURPOSE: To return the name of this DataType.  If we are a keyword, the DataType will be 
        //properly formatted.
        ///
        public string ToName()
        {
            if (this.IsEntity && this.Entity.IsNameKeyword)
            {
                return "[" + this.mProperty.DataType + "]";
            }
            else
            {
                return this.mProperty.DataType;
            }
        }

        public string ToX()
        {
            if (this.mProperty.IsNullable && !(this.mProperty.DataType.ToLower() == "string") && !this.IsEntity)
            {
                return this.mProperty.DataType + "?";
            }
            else
            {
                if (this.IsEntity && this.Entity.IsNameKeyword)
                {
                    return "[" + this.mProperty.DataType + "]";
                }
                else
                {
                    return this.mProperty.DataType;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public string ToDtoField()
        {
            //If Me.mProperty.IsNullable Then
            //    Return Me.mProperty.DataType & "?"
            //Else
            //    Return Me.mProperty.DataType
            //End If


            if (!this.mProperty.IsNullable || this.ToName() == "string")
            {
                return this.mProperty.DataType;
            }
            else
            {
                return this.mProperty.DataType + "?";
            }

        }


        ///
        //NAME: ToProperty
        //PURPOSE: To return the name of this DataType as a strongly typed property (e.g. Person
        //goes to PersonProperty, String goes to StringProperty).
        ///
        public string ToProperty()
        {
            return this.mProperty.DataType + "Property";
        }


        ///
        //NAME: ToType
        ///
        public string ToType()
        {
            if (this.mProperty.IsEntityType)
            {
                return "Guid";

            }
            else if (this.mProperty.DataType == "Binary")
            {
                return "Byte()";

            }
            else
            {
                if (this.mProperty.IsNameKeyword)
                {
                    return "[" + this.mProperty.DataType + "]";
                }
                else
                {
                    return this.mProperty.DataType;
                }

            }
        }

        ///
        //NAME: ToBaseName
        //PURPOSE: To identify the base name of this property if it is a collection property (in which
        //case the name has the word Collection appended to it.)
        ///
        public string ToBaseName()
        {
            return this.mProperty.DataType.Replace("Collection", "");
        }

        public string ToBaseNameQualified()
        {
            if (this.IsEntity && this.Entity.IsNameKeyword)
            {
                return "[" + this.mProperty.DataType.Replace("Collection", "") + "]";
            }
            else
            {
                return this.mProperty.DataType.Replace("Collection", "");
            }
        }

    }
}

